<?php
/**
 * Copyright 2011  SURFfoundation
 * 
 * This file is part of ESCAPE.
 * 
 * ESCAPE is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * ESCAPE is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with ESCAPE.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * For more information:
 * http://escapesurf.wordpress.com/
 * http://www.surffoundation.nl/
 * 
 * Contact: d.vierkant@utwente.nl
 * 
 * @license http://www.gnu.org/licenses/gpl.html GNU GPLv3
 */
?>
<?php
/**
 * URL content
 * 
 * Fetch the content from a URL, limited by size and time.
 */
class metaextractor_UrlContent
{
	protected $url = null;
	protected $mimeType = null;
	protected $charset = null;
	protected $content = '';
	protected $complete = false;
	protected $httpHeaders = null;
	
	/**
	 * Fetch the content of a URL.
	 * @param array $config Meta extractor config
	 * @param string $url URL
	 * @throws metaextractor_exception_InvalidUrl
	 * @throws metaextractor_exception_ReadFailure
	 */
	public function __construct($config, $url)
	{
		// only accept HTTP and HTTPS URLs
		if(!preg_match('~^https?://~', $url))
		{
			throw new metaextractor_exception_InvalidUrl('Invalid URL: ' . $url);
		}
		
		// store the URL
		$this->url = $url;
		
		// set the connection parameters
		$httpOptions = array(
							'timeout ' => $config['read_timeout'],
							'header' => $config['http_headers'],
						);
		if($config['proxy_server_url'] !== null)
		{
			// proxy is required
			// the PHP HTTP wrappers seem to expect tcp: instead of the usual http:
			$httpOptions['proxy'] = str_replace('http:', 'tcp:', $config['proxy_server_url']);
			$httpOptions['request_fulluri'] = true;
		}
		$streamContext = stream_context_create(array('http' => $httpOptions));
		
		// open a connection
		$handle = fopen($url, 'rb', false, $streamContext);
		if($handle)
		{
			// fetch the stream metadata
			$streamMetaData = stream_get_meta_data($handle);
			if($streamMetaData['wrapper_type'] == 'http')
			{
				// fetch the wrapper specific metadata
				$this->httpHeaders = $streamMetaData['wrapper_data'];
				
				// parse the HTTP headers
				foreach($this->httpHeaders as $httpHeader)
				{
					// check if we can find a MIME type in the HTTP headers
					if(preg_match('/^Content-Type: ([^;]+)/', $httpHeader, $matches))
					{
						$this->mimeType = $matches[1];
						
						if(preg_match('/charset ?=[\'"]?([-_a-z0-9]+)[\'"]?/i', $httpHeader, $matches))
						{
							$this->charset = strtolower($matches[1]);
						}
					}
				}
				
				// fetch the content, check for time and size limits
				$startTime = microtime(true);
				while(!feof($handle) && strlen($this->content) < $config['content_limit'] && (microtime(true) - $startTime) < $config['fetch_timeout'])
				{
					$this->content .= fread($handle, 1024);
				}
				
				// did we receive the complete content?
				if(feof($handle))
				{
					$this->complete = true;
				}
				
				fclose($handle);
			}
			else
			{
				// this isn't a HTTP request, this shouldn't happen, it might be a hacking attempt
				fclose($handle);
				
				throw new metaextractor_exception_ReadFailure('Unable to read from URL: ' . $url);
			}
		}
		else
		{
			// failed to open the connection
			throw new metaextractor_exception_ReadFailure('Unable to read from URL: ' . $url);
		}
	}
	
	/**
	 * Get the URL.
	 * @return string URL
	 */
	public function getUrl()
	{
		return $this->url;
	}
	
	/**
	 * Get the MIME-type indicated by the server. 
	 * @return string MIME-type indicated by the server, null if no MIME-type was provided
	 */
	public function getMimeType()
	{
		return $this->mimeType;
	}
	
	/**
	 * Get the charset indicated by the server. 
	 * @return string charset indicated by the server, null if no charset was provided
	 */
	public function getCharset()
	{
		return $this->charset;
	}
	
	/**
	 * Get the text content returned by the server. If a charset other than UTF-8 is indicated by the server, the content is converted to UTF-8.
	 * @return string text content returned by the server
	 */
	public function getUtf8TextContent()
	{
		if($this->charset != 'utf-8' || $this->charset != 'utf8')
		{
			return iconv($this->charset, 'utf-8//IGNORE', $this->content);
		}
		else
		{
			return $this->content;
		}
	}
	
	/**
	 * Get the content returned by the server.
	 * @return string content returned by the server
	 */
	public function getContent()
	{
		return $this->content;
	}
	
	/**
	 * Get the download completion status.
	 * @return boolean true if the download completed, false if the size or time limit was exceeded
	 */
	public function isComplete()
	{
		return $this->complete;
	}
	
	/**
	 * Get the HTTP headers returned by the server.
	 * @return array HTTP headers returned by the server
	 */
	public function getHttpHeaders()
	{
		return $this->httpHeaders;
	}
}